package  de.tum.softwarengineering1.ws201112.team11.sudoku.gui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JRadioButton;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import de.tum.softwarengineering1.ws201112.team11.sudoku.event.SudokuBoardChangeEvent;
import de.tum.softwarengineering1.ws201112.team11.sudoku.model.Cell;
import de.tum.softwarengineering1.ws201112.team11.sudoku.model.SudokuBoard;
import de.tum.softwarengineering1.ws201112.team11.sudoku.model.SudokuController;
import de.tum.softwarengineering1.ws201112.team11.sudoku.model.SudokuController.Strategies;
import de.tum.softwarengineering1.ws201112.team11.sudoku.strategy.EasyDifficulty;
import de.tum.softwarengineering1.ws201112.team11.sudoku.strategy.HardDifficulty;
import de.tum.softwarengineering1.ws201112.team11.sudoku.strategy.MediumDifficulty;
import de.tum.softwarengineering1.ws201112.team11.sudoku.util.DifficultyContext;

public class SudokuView extends JFrame implements Observer {

    /**
     * version string to avoid warning
     */
    private static final long serialVersionUID = 1L;
    
    private SudokuTable sudokuTable;
    private SudokuBoard board;

    JRadioButton radioRandom;
    JRadioButton radioBacktracking;
    JRadioButton radioBruteforce;

    JButton buttonSolve;
    JButton buttonReset;
    JButton buttonNewSudoku;
    
    JComboBox boxLevelSelector;

    ButtonGroup grp;
    private SudokuController controller;

    public SudokuView(SudokuBoard board, SudokuController controller) {
		super("Sudoku");
		this.controller = controller;
		this.board = board;
		initView();
    }

    public void initView() {
		GridBagLayout layout = new GridBagLayout();
		getContentPane().setLayout(layout);
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.HORIZONTAL;
	
		sudokuTable = new SudokuTable(board.getSize());
		sudokuTable.addTableModelListener(controller);
	
		c.gridx = 1;
		c.gridy = 0;
		c.gridwidth = 2;
		c.gridheight = 7;
		layout.setConstraints(sudokuTable, c);
		getContentPane().add(sudokuTable);
	
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.anchor = GridBagConstraints.NORTH;
		
	
		/**
		 * Buttons
		 */
		ActionListener buttonListener = new ButtonClickListener();
		
		buttonNewSudoku = new JButton("New Sudoku!");
		buttonNewSudoku.setActionCommand("new");
		buttonNewSudoku.addActionListener(buttonListener);
		layout.setConstraints(buttonNewSudoku, c);
		getContentPane().add(buttonNewSudoku);
		
		++c.gridy;
		ActionListener boxListener = new ComboBoxListener();
		String[] items = {"Easy","Medium","Hard"};
		boxLevelSelector = new JComboBox(items);
		boxLevelSelector.addActionListener(boxListener);
		layout.setConstraints(boxLevelSelector, c);
		getContentPane().add(boxLevelSelector);
		
		++c.gridy;
		buttonReset = new JButton("Reset!");
		buttonReset.setActionCommand("reset");
		buttonReset.addActionListener(buttonListener);
		layout.setConstraints(buttonReset, c);
		getContentPane().add(buttonReset);
		
		++c.gridy;
		buttonSolve = new JButton("Solve!");
		buttonSolve.setActionCommand("solve");
		buttonSolve.addActionListener(buttonListener);
		layout.setConstraints(buttonSolve, c);
		getContentPane().add(buttonSolve);
		

		/**
		 * Radio Buttons
		 */
		SolverRadioChangeListener listener = new SolverRadioChangeListener();
			
		grp = new ButtonGroup();
		c.gridx = 0;
		c.gridy = 3;
		c.gridwidth = 1;
	
		radioRandom = new JRadioButton("Random", true);
		radioRandom.addChangeListener(listener);
		grp.add(radioRandom);
		++c.gridy;
		layout.setConstraints(radioRandom, c);
		getContentPane().add(radioRandom);
			
		radioBacktracking = new JRadioButton("Backtracking", false);
		radioBacktracking.addChangeListener(listener);
		grp.add(radioBacktracking);
		++c.gridy;
		layout.setConstraints(radioBacktracking, c);
		getContentPane().add(radioBacktracking);
	
		radioBruteforce = new JRadioButton("Brute Force", false);
		radioBruteforce.addChangeListener(listener);
		grp.add(radioBruteforce);
		++c.gridy;
		layout.setConstraints(radioBruteforce, c);
		getContentPane().add(radioBruteforce);
	
		this.pack();
		this.setVisible(true);
    }

    @Override
    public void update(Observable observed, Object event) 
    {
    	if (!(observed instanceof SudokuBoard && board == observed))
    		return;

    	if (!(event instanceof SudokuBoardChangeEvent))
    		return;

    	SudokuBoardChangeEvent changeEvent = (SudokuBoardChangeEvent) event;
    	if (changeEvent.isCompleteRefreshRequired()) {
    		completeBoardRefresh();
    	} 
    	else {
    		for (Cell cell : changeEvent.getChangedCells()) {
    			sudokuTable.setValueAt(cell.getValue(), cell.getLine(), cell.getColumn());
    		}
    	}
	}

    private void completeBoardRefresh() 
    {
		sudokuTable.reset();
		for (int row = 0; row < board.getSize(); row++) {
		    for (int col = 0; col < board.getSize(); col++) {
				sudokuTable.setValueAt(board.getValue(row, col), row, col);
				if (board.getFixedCells().contains(new Cell(row, col))) {
				    sudokuTable.setFixed(row, col);
				}
		    }
		}
    }
	
    public void solveClicked() {
		boolean solved = board.solve();
		if (!solved) {
		    int res = JOptionPane
			    .showConfirmDialog(
				    this,
				    "Sudoku cannot besolved with the values that are already filled in.\n Do you want to reset the board?",
				    "Not solvable.", JOptionPane.YES_NO_OPTION);
		    if (res == JOptionPane.YES_OPTION) {
			System.out.println("Reset.");
			board.reset();
		    }
		}
    }
    
    private class ButtonClickListener implements ActionListener 
    {
		@Override
		public void actionPerformed(ActionEvent e) {
		    if ("solve".equals(e.getActionCommand())) {
		    	solveClicked();
		    } 
		    else if ("new".equals(e.getActionCommand())) {
		    	board.newBoard();
		    } else if ("reset".equals(e.getActionCommand())) {
		    	board.reset();
		    }
	
		}
    }
    
    private class SolverRadioChangeListener implements ChangeListener 
    {
		@Override
		public void stateChanged(ChangeEvent e) 
		{
		    if (radioBruteforce.isSelected()) {
		    	controller.setStrategyChoice(Strategies.BRUTE_FORCE);
		    } 
		    else if (radioRandom.isSelected()) {	    
		    	controller.setStrategyChoice(Strategies.RANDOMIZED);
		    } 
		    else if (radioBacktracking.isSelected()) {
		    	controller.setStrategyChoice(Strategies.BACK_TRACKING);
		    }
		}
    }

    
    private class ComboBoxListener implements ActionListener
    {
    	@Override
		public void actionPerformed(ActionEvent e) {
    		
    		JComboBox box = (JComboBox) e.getSource();
    		String selection = (String) box.getSelectedItem();
    		
		    if (selection.toLowerCase().equals("easy")) 
		    {
		    	DifficultyContext.getDifficultyContext().setDifficulty(new EasyDifficulty());		    } 
		    else if (selection.toLowerCase().equals("medium")) 
		    {
		    	DifficultyContext.getDifficultyContext().setDifficulty(new MediumDifficulty());	
		    }
		    else if (selection.toLowerCase().equals("hard")) 
		    {
		    	DifficultyContext.getDifficultyContext().setDifficulty(new HardDifficulty());	
		    }
	
		}
    }
    
}
